package com.study.sqlSession;

import com.study.config.BoundSql;
import com.study.io.Resources;
import com.study.pojo.Configuration;
import com.study.pojo.MappedStatement;
import com.study.utils.GenericTokenParser;
import com.study.utils.ParameterMapping;
import com.study.utils.ParameterMappingTokenHandler;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

public class ExecutorImpl implements Executor {

    private PreparedStatement common (Configuration configuration, MappedStatement mappedStatement, Object param) throws Exception {
        // 注册驱动，获取链接
        Connection connection = configuration.getDataSource().getConnection();
        // 获取sql
        String sql = mappedStatement.getSql();
        // 转换sql
        BoundSql boundSql = getBoundSql(sql);
        // 获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getParseSql());
        // 设置参数
        String parameterType = mappedStatement.getParameterType();
        Class<?> parameterTypeClass = Resources.getClass(parameterType);

        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            String content = parameterMapping.getContent();
            if (parameterTypeClass.getClassLoader() == null) {
                // 如果不是自己定义的类
                preparedStatement.setObject(i + 1, param);
            } else {
                // 反射
                Field field = parameterTypeClass.getDeclaredField(content);
                field.setAccessible(true);
                Object object = field.get(param);
                preparedStatement.setObject(i + 1, object);
            }
        }
        return preparedStatement;
    }
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object args) throws Exception {
        PreparedStatement preparedStatement = common(configuration, mappedStatement, args);

        // 执行sql
        String resultType = mappedStatement.getResultType();
        Class<?> resultTypeClass = Resources.getClass(resultType);
        Method[] methods = resultTypeClass.getMethods();

        ResultSet resultSet = preparedStatement.executeQuery();
        ResultSetMetaData metaData = resultSet.getMetaData();

        List<Object> list = new ArrayList<>();
        while(resultSet.next()) {
            Object object = resultTypeClass.newInstance();
            for(int i = 1; i <= metaData.getColumnCount(); i++) {
                // 字段名
                String columnName = metaData.getColumnName(i);
                // 字段值
                Object value = resultSet.getObject(columnName);
                for (Method method : methods) {
                    if (method.getName().equalsIgnoreCase("set" + columnName)) {
                        method.invoke(object, value);
                    }
                }
            }
            list.add(object);
        }
        return (List<E>) list;
    }

    @Override
    public int update(Configuration configuration, MappedStatement mappedStatement, Object args) throws Exception {
        PreparedStatement preparedStatement = common(configuration, mappedStatement, args);
        int result = preparedStatement.executeUpdate();
        return result;

    }

    private BoundSql getBoundSql(String sql) {
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        String parseSql = genericTokenParser.parse(sql);
        BoundSql boundSql = new BoundSql(parseSql, parameterMappingTokenHandler.getParameterMappings());
        return boundSql;
    }


}
